package com.jblv.frame.common.utils;

import com.google.gson.*;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Type;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author happyonion.yangtao
 * @version 1.0
 * @title GsonUtils
 * @projectName jblv-frame
 * @packageName com.jblv.common.utils
 * @description GsonUtils
 * @date Create at 2020/5/7 11:06
 * @since jdk1.8
 */

public class GsonUtils {
    private static final Pattern HUMP_PATTERN = Pattern.compile("[A-Z]");
    private static final Pattern UNDERLINE_PATTERN = Pattern.compile("_[a-z]");
    private static final Pattern UNDERLINE_MARGIN_PATTERN = Pattern.compile("(margin)(_)(left|right|top|bottom)");
    private static final Pattern MINUS_MARGIN_PATTERN = Pattern.compile("(margin)(-)(left|right|top|bottom)");
    private static final Pattern UNDERLINE_BACKGROUND_PATTERN = Pattern.compile("(background)(_)(image|size|repeat|color)");
    private static final Pattern MINUS_BACKGROUND_PATTERN = Pattern.compile("(background)(-)(image|size|repeat|color)");

    public GsonUtils() {
    }

    public static JsonObject objectToJSON(Object obj) {
        Gson gson = new Gson();
        return (JsonObject)gson.toJsonTree(obj);
    }

    public static <T> T mergeObject(T sourceObject, T destObject) {
        JsonObject sourceJson = null;
        JsonObject destJson = null;
        if (sourceObject instanceof JsonObject) {
            sourceJson = (JsonObject)sourceObject;
            destJson = (JsonObject)destObject;
        } else {
            Gson gson = new Gson();
            sourceJson = gson.toJsonTree(sourceObject).getAsJsonObject();
            destJson = gson.toJsonTree(destObject).getAsJsonObject();
            gson = null;
        }

        Iterator<Map.Entry<String, JsonElement>> iter = destJson.entrySet().iterator();
        Set set = sourceJson.entrySet();

        while(iter.hasNext()) {
            Map.Entry<String, JsonElement> item = (Map.Entry)iter.next();
            String fieldName = (String)item.getKey();
            JsonElement obj = (JsonElement)item.getValue();
            if (set.contains(fieldName) && null != obj) {
                sourceJson.add(fieldName, obj);
            }
        }

        Class<T> cls = (Class<T>) sourceObject.getClass();
        return jsonToBean(sourceJson, cls);
    }

    public static JsonObject excludesProperty(JsonObject jsonObj, String[] excludes) {
        String[] var2 = excludes;
        int var3 = excludes.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String excludeName = var2[var4];
            if (jsonObj.has(excludeName)) {
                jsonObj.remove(excludeName);
            }
        }

        return jsonObj;
    }

    public static JsonObject getCombineJson(JsonObject newJson, Object oldObject) {
        Gson gson = new Gson();
        JsonObject oldJson = (JsonObject)gson.toJsonTree(oldObject);
        gson = null;
        Iterator<Map.Entry<String, JsonElement>> iter = newJson.entrySet().iterator();
        Set set = oldJson.entrySet();

        while(iter.hasNext()) {
            Map.Entry<String, JsonElement> item = (Map.Entry)iter.next();
            String fieldName = (String)item.getKey();
            JsonElement obj = (JsonElement)item.getValue();
            if (set.contains(fieldName)) {
                oldJson.add(fieldName, obj);
            }
        }

        return oldJson;
    }

    public static List<String> getKeys(JsonObject json) {
        if (null == json) {
            return null;
        } else {
            List<String> keyList = new ArrayList();
            Iterator it = json.entrySet().iterator();

            while(it.hasNext()) {
                keyList.add(((Map.Entry)it.next()).getKey().toString());
            }

            return keyList;
        }
    }

    public static <T> T jsonToBean(String sourceJson, Type type) {
        Gson gson = new Gson();
        JsonParser jsonParser = new JsonParser();
        JsonElement jse = jsonParser.parse(sourceJson);
        T obj = gson.fromJson(jse, type);
        gson = null;
        return obj;
    }

    public static <T> T jsonToBean(JsonElement sourceJson, Type type) {
        Gson gson = new Gson();
        T obj = gson.fromJson(sourceJson, type);
        gson = null;
        return obj;
    }

    public static <T> T jsonToBean(String sourceJson, Class<T> sourceClass) {
        Gson gson = new Gson();
        T obj = gson.fromJson(sourceJson, sourceClass);
        gson = null;
        return obj;
    }

    public static <T> T jsonToBean(JsonElement sourceJson, Class<T> sourceClass) {
        Gson gson = new Gson();
        T obj = gson.fromJson(sourceJson, sourceClass);
        gson = null;
        return obj;
    }

    public static <T> T jsonToBean(String jsonStr, Class<T> sourceClass, String dateFormat) {
        if (StringUtils.isEmpty(dateFormat)) {
            return jsonToBean(jsonStr, sourceClass);
        } else {
            Gson gson = (new GsonBuilder()).setDateFormat(dateFormat).create();
            T obj = gson.fromJson(jsonStr, sourceClass);
            gson = null;
            return obj;
        }
    }

    public static <T> T jsonToBean(JsonElement sourceJson, Class<T> sourceClass, String dateFormat) {
        if (StringUtils.isEmpty(dateFormat)) {
            return jsonToBean(sourceJson, sourceClass);
        } else {
            Gson gson = (new GsonBuilder()).setDateFormat(dateFormat).create();
            T obj = gson.fromJson(sourceJson, sourceClass);
            gson = null;
            return obj;
        }
    }

    public static String getJsonStr(Object obj) {
        Gson gson = new Gson();
        String resultStr = gson.toJson(obj);
        gson = null;
        return resultStr;
    }

    public static String getJsonStrUnEscape(Object obj) {
        GsonBuilder gb = new GsonBuilder();
        gb.disableHtmlEscaping();
        return gb.create().toJson(obj);
    }

    public static JsonObject getJsonObject(String jsonStr) {
        JsonParser parser = new JsonParser();
        return StringUtils.isNotEmpty(jsonStr) ? parser.parse(jsonStr).getAsJsonObject() : null;
    }

    public static JsonArray getJsonArray(String jsonStr) {
        JsonParser parser = new JsonParser();
        return parser.parse(jsonStr).getAsJsonArray();
    }

    public static JsonObject getHumpToUnderlineJsonObject(JsonObject sourceJson) {
        JsonObject destJson = getHumpToUnderlineJsonElement((JsonElement)sourceJson).getAsJsonObject();
        return destJson;
    }

    public static JsonObject getUnderlineToHumpJsonObject(JsonObject sourceJson) {
        JsonObject destJson = getUnderlineToHumpJsonElement((JsonElement)sourceJson).getAsJsonObject();
        return destJson;
    }

    public static JsonArray getHumpToUnderlineJsonArray(JsonArray sourceJson) {
        JsonArray destJson = getHumpToUnderlineJsonElement((JsonElement)sourceJson).getAsJsonArray();
        return destJson;
    }

    public static JsonArray getUnderlineToHumpJsonArray(JsonArray sourceJson) {
        JsonArray destJson = getUnderlineToHumpJsonElement((JsonElement)sourceJson).getAsJsonArray();
        return destJson;
    }

    public static JsonElement getUnderlineToHumpJsonElement(String sourceJsonStr) {
        JsonParser parser = new JsonParser();
        JsonElement jsonElement = parser.parse(sourceJsonStr);
        return getUnderlineToHumpJsonElement(jsonElement);
    }

    public static JsonElement getUnderlineToHumpJsonElement(JsonElement jsonElement) {
        return getTranslateJsonElement(jsonElement, GsonUtils.TranslateType.UNDERLINE_TO_HUMP);
    }

    public static JsonElement getHumpToUnderlineJsonElement(String sourceJsonStr) {
        JsonParser parser = new JsonParser();
        JsonElement jsonElement = parser.parse(sourceJsonStr);
        return getHumpToUnderlineJsonElement(jsonElement);
    }

    public static JsonElement getHumpToUnderlineJsonElement(JsonElement jsonElement) {
        return getTranslateJsonElement(jsonElement, GsonUtils.TranslateType.HUMP_TO_UNDERLINE);
    }

    private static JsonElement getTranslateJsonElement(JsonElement jsonElement, GsonUtils.TranslateType type) {
        if (jsonElement instanceof JsonArray) {
            JsonArray jsonArray = jsonElement.getAsJsonArray();
            JsonArray newJsonArray = new JsonArray();
            Iterator var4 = jsonArray.iterator();

            while(var4.hasNext()) {
                JsonElement item = (JsonElement)var4.next();
                newJsonArray.add(getTranslateJsonElement(item, type));
            }

            int oldJsonArraySize = jsonArray.size();

            for(int i = oldJsonArraySize - 1; i >= 0; --i) {
                jsonArray.remove(i);
            }

            jsonArray.addAll(newJsonArray);
        } else if (jsonElement instanceof JsonObject) {
            return translate(jsonElement.getAsJsonObject(), type);
        }

        return jsonElement;
    }

    private static JsonObject translate(JsonObject sourceJson, GsonUtils.TranslateType translateType) {
        JsonObject destJson = new JsonObject();

        String fieldName;
        Object fieldValue;
        for(Iterator iter = sourceJson.entrySet().iterator(); iter.hasNext(); destJson.add(fieldName, (JsonElement)fieldValue)) {
            Map.Entry<String, JsonElement> jsonItem = (Map.Entry)iter.next();
            fieldName = (String)jsonItem.getKey();
            fieldValue = (JsonElement)jsonItem.getValue();
            if (fieldValue instanceof JsonObject) {
                fieldValue = translate((JsonObject)fieldValue, translateType);
            } else if (fieldValue instanceof JsonArray) {
                JsonArray valueArray = (JsonArray)fieldValue;
                fieldValue = translateJsonArray(valueArray, translateType);
            }

            switch(translateType) {
                case UNDERLINE_TO_HUMP:
                    fieldName = underLineToHumpField(fieldName);
                    break;
                case HUMP_TO_UNDERLINE:
                    fieldName = humpToUderlineField(fieldName);
            }
        }

        sourceJson = null;
        return destJson;
    }

    private static JsonArray translateJsonArray(JsonArray sourceJsonArray, GsonUtils.TranslateType translateType) {
        JsonArray destJsonArray = new JsonArray();
        Iterator var3 = sourceJsonArray.iterator();

        while(var3.hasNext()) {
            JsonElement valueItem = (JsonElement)var3.next();
            if (valueItem instanceof JsonObject) {
                destJsonArray.add(translate(valueItem.getAsJsonObject(), translateType));
            } else if (valueItem instanceof JsonArray) {
                destJsonArray.add(translateJsonArray(valueItem.getAsJsonArray(), translateType));
            } else {
                destJsonArray.add(valueItem);
            }
        }

        return destJsonArray;
    }

    private static String humpToUderlineField(String fieldName) {
        Matcher matcher = HUMP_PATTERN.matcher(fieldName);
        StringBuffer sb = new StringBuffer();

        String tmpFieldName;
        while(matcher.find()) {
            tmpFieldName = matcher.group();
            matcher.appendReplacement(sb, "_" + tmpFieldName.toLowerCase());
        }

        matcher.appendTail(sb);
        tmpFieldName = sb.toString();
        matcher = UNDERLINE_MARGIN_PATTERN.matcher(tmpFieldName);
        tmpFieldName = matcher.replaceAll("$1-$3");
        matcher = UNDERLINE_BACKGROUND_PATTERN.matcher(tmpFieldName);
        tmpFieldName = matcher.replaceAll("$1-$3");
        return tmpFieldName;
    }

    private static String underLineToHumpField(String fieldName) {
        Matcher matcher = MINUS_MARGIN_PATTERN.matcher(fieldName);
        String tmpFieldName = matcher.replaceAll("$1_$3");
        matcher = MINUS_BACKGROUND_PATTERN.matcher(tmpFieldName);
        tmpFieldName = matcher.replaceAll("$1_$3");
        matcher = UNDERLINE_PATTERN.matcher(tmpFieldName);
        StringBuffer sb = new StringBuffer();

        while(matcher.find()) {
            String g = matcher.group();
            if (g.length() == 2) {
                matcher.appendReplacement(sb, g.substring(1).toUpperCase());
            }
        }

        matcher.appendTail(sb);
        return sb.toString();
    }

    static enum TranslateType {
        UNDERLINE_TO_HUMP,
        HUMP_TO_UNDERLINE;

        private TranslateType() {
        }
    }
}
